package org.goobs.threading;

import java.util.HashMap;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;

import org.goobs.utils.SparseList;



public class Lock implements Comparable <Lock>{
	private static volatile SparseList <Set <Lock>> locks = new SparseList<Set<Lock>>();
	private static volatile HashMap <Integer, PriorityQueue<Lock>> lastHeld = new HashMap <Integer, PriorityQueue<Lock>>();
	private static int nextGroup = 0;
	
	
	private Thread holdingThread = null;
	private int group;
	private int order = -1;
	
	public Lock(){
		
	}
	
	public Lock(int group, int order){
		if(!locks.hasIndex(group)){
			locks.set(group, new HashSet<Lock>());
		}
		Set <Lock> groupSet = locks.get(group);
		groupSet.add(this);
		this.group = group;
		if(order < 0){
			throw new IllegalArgumentException("negative ordering for lock");
		}
		this.order = order;
	}
	
	public synchronized void acquire(){
		//--Check if it's the current thread
		if(Thread.currentThread() == holdingThread){
			throw new IllegalStateException("Thread " + Thread.currentThread().getId() + " is trying to acquire a lock it already owns");
		}
		//--Possibly check if the order is correct
		if(order >= 0){
			PriorityQueue <Lock> queue = lastHeld.get(this.group);
			int last = queue.peek().order;
			if(this.order < last){
				throw new ConcurrencyException("Aquiring lock out of order");
			}
			queue.add(this);
		}
		//--Acquire lock
		while(holdingThread != null){
			try {
				this.wait();
			} catch (InterruptedException e) {
				throw new IllegalStateException("Thread was interrupted while waiting for lock");
			}
		}
		holdingThread = Thread.currentThread();
	}
	
	public synchronized void release(){
		if(Thread.currentThread() != holdingThread){
			throw new IllegalStateException("Trying to release a lock which is not owned by this thread");
		}
		//--Possibly take this lock out of the thread order
		if(order >= 0){
			lastHeld.get(this.group).remove(this);
		}
		holdingThread = null;
		this.notifyAll();
	}
	
	public synchronized boolean isHeldByCurrentThread(){
		return Thread.currentThread() == holdingThread;
	}

	@Override
	public int compareTo(Lock arg0) {
		return this.order - arg0.order;
	}
	
	@Override
	public boolean equals(Object o){
		if(!(o instanceof Lock)){
			return false;
		}
		return this.order == ((Lock) o).order;
	}
	
	public static int nextGroup(){
		return nextGroup++;
	}
	
}
